#!python
# (C) Copyright 2016-2021 Intel Corporation.
#
# SPDX-License-Identifier: BSD-2-Clause-Patent
#
"""Build CaRT components"""

import os
import sys
import daos_build
# pylint: disable=no-name-in-module
# pylint: disable=import-error
# pylint: disable=ungrouped-imports
import SCons.Action

HEADERS = ['api.h', 'iv.h', 'types.h', 'swim.h']

SRC = ['crt_bulk.c', 'crt_context.c', 'crt_corpc.c',
       'crt_ctl.c', 'crt_debug.c', 'crt_group.c', 'crt_hg.c', 'crt_hg_proc.c',
       'crt_init.c', 'crt_iv.c', 'crt_register.c',
       'crt_rpc.c', 'crt_self_test_client.c', 'crt_self_test_service.c',
       'crt_swim.c', 'crt_tree.c', 'crt_tree_flat.c', 'crt_tree_kary.c',
       'crt_tree_knomial.c', 'crt_hlc.c', 'crt_hlct.c']

LIB_SRC = ['crt_utils.c']

####################################
# Create cart utility share objects
####################################
def build_utility_shared_obj(denv):
    """Build cart utility library"""
    cart_utils_objs  = denv.SharedObject(LIB_SRC)
    Export('cart_utils_objs')

# pylint: disable=unused-argument
def macro_expand(target, source, env):
    """Function for PostAction"""
    scope = r"'/struct [^ ]*_\(in\|out\) {/,/};/p'"
    sed_e = r"-e 's/\s\s*/ /g' -e 's/};struct /};\nstruct /g'"
    sed_d = r"-e 's/\([{;]\) /\1\t/g' -e 's/\([{;]\)/\1\n/g'"
    grepv = r"'struct sockaddr_in {'"
    tgts = ""
    for tgt in target:
        tgts += "%s_grep " % tgt.abspath
        os.system("sed -n %s %s | tr -d '\\n' | sed %s > %s_grep"
                  % (scope, tgt.abspath, sed_e, tgt.abspath))
    h_name = "src/cart/_structures_from_macros_.h"
    h_file = os.path.join(Dir('#').abspath, h_name)
    with open(h_file, "w") as outfile:
        outfile.write("/* Automatically generated with structures\n"
                      " * expanded from CRT_RPC_DECLARE() macros\n *\n")
        with open("LICENSE", "r") as infile:
            for line in infile.readlines():
                if line == "\n":
                    outfile.write(" *\n")
                else:
                    outfile.write(" * " + line)
            infile.close()
        outfile.write(" */\n\n")
        outfile.close()
    if tgts != "":
        os.system("cat %s | grep -v %s | sort -u | sed %s >> %s"
                  % (tgts, grepv, sed_d, h_file))
# pylint: enable=unused-argument

def scons():
    """Scons function"""

    # Generate the common libraries used by everyone
    SConscript('swim/SConscript')
    Default('swim')

    Import('env', 'prereqs', 'swim_targets', 'gurt_lib', 'CART_VERSION')
    Import('conf_dir', 'base_env')

    base_env.AppendUnique(LIBPATH=[Dir('.')])

    env.Alias('install', '$PREFIX')

    # There is probably a better way to do this but let's get it linking first
    env.AppendUnique(LIBPATH=[Dir('.')])
    env.AppendUnique(LINKFLAGS=["-Wl,-rpath-link=%s" % Dir('.').path])
    env.AppendUnique(CPPPATH=[Dir('.').srcnode()])
    env.AppendUnique(CPPPATH=[Dir('include').srcnode()])
    for header in HEADERS:
        env.Install(os.path.join('$PREFIX', 'include', 'cart'),
                    os.path.join('../include', 'cart', header))

    prereqs.require(env, 'mercury', headers_only=True)

    denv = env.Clone(LIBS=[])

    # Generate cart utility shared objects
    build_utility_shared_obj(denv)

    denv.AppendUnique(LIBS=['gurt'])
    prereqs.require(denv, 'mercury')

    cart_targets = denv.SharedObject(SRC)

    compiler = env.get('COMPILER').lower()
    if compiler != 'covc':
        pp_env = denv.Clone()

        # pylint: disable=no-member
        pp_files = pp_env.Preprocess(SRC)
        pp_env.AddPostAction(pp_files, SCons.Action.Action(macro_expand, None))
        # pylint: enable=no-member

        denv.Requires(cart_targets, pp_files)

    cart_lib = daos_build.library(denv, 'libcart', [cart_targets, swim_targets],
                                  SHLIBVERSION=CART_VERSION)
    denv.Requires(cart_lib, [swim_targets, gurt_lib])
    denv.InstallVersionedLib('$PREFIX/lib64/', cart_lib,
                             SHLIBVERSION=CART_VERSION)

    Default(cart_lib)
    Export('cart_lib', 'cart_targets')

    if not env.GetOption('clean') and not env.GetOption('help'):

        print("Checking local headers can be included")
        broken_includes = False
        config_env = env.Clone()
        config = Configure(config_env)
        for header in HEADERS:
            if not config.CheckHeader(os.path.join('cart', header)):
                broken_includes = True
        config.Finish()

        if broken_includes:
            print("Broken local header files, cannot continue")
            Exit(2)

    Export('env', 'prereqs')

    env.Install(conf_dir, ['utils/memcheck-cart.supp',
                           'utils/fault-inject-cart.yaml'])


if __name__ == "SCons.Script":
    scons()
